#pragma once
#include "Socket.hpp"
#include "Connection.hpp"
#include"Channel.hpp"
#include"Util.hpp"

class Listener : public Connection
{
public:
    Listener(uint16_t port)
        : _listensockfd(std::make_unique<TcpSocket>()), _port(port)
    {
        // 创建监听套接字
        _listensockfd->BuildListenSocket(_port);
        // 填写基类中的成员属性
        // _sockfd = _listensockfd->Sockfd();
        SetSockfd(_listensockfd->Sockfd());
        // _events = EPOLLIN;//关心读事件
        // 此处使用ET模式，将关心的事件中设置EPOLLET；并且需要将该文件描述符设置为非阻塞状态
        SetEvents(EPOLLIN | EPOLLET);
    }
    ~Listener() {}
    // 实现基类中的虚方法
    void Recver() override
    {
        // 由于此处是ET模式，可能会有多条连接到来，所以需要循环获取连接
        while (true)
        {
            // 获取连接
            InetAddr clientaddr;
            int error = 0;
            int sockfd = _listensockfd->Accept(&clientaddr , &error);
            if (sockfd < 0)
            {
                // 获取连接失败,由于是非阻塞状态，需要根据错误码分情况讨论
                if(error == EAGAIN)
                {
                    //表示没有连接可以再获取
                    break;
                }
                else if(error == EINTR)
                {
                    //信号中断
                    continue;
                }
                else{
                    //真正的出错
                    break;
                }
            }

            //获取新连接成功，需要将新连接封装成Connection （对于普通socket 再封装成一个模块）,然后添加进 _connections 中（借助于回指指针）
            SetNonBlock(sockfd);//将新连接设置为非阻塞
            std::shared_ptr<Connection> conn = std::make_shared<Channel>(sockfd , clientaddr);
            conn->SetCallback(_cb);//将回调函数设置进连接中
            //利用回指指针将新连接放入Reactor 的哈希表中
            Owner()->AddConnection(conn);
        }
    }
    //对于监听套接字来说不用实现Sender 和 Excepter
    void Sender() override
    {
    }
    void Excepter() override
    {
    }

private:
    // 监听套接字
    std::unique_ptr<Socket> _listensockfd;
    uint16_t _port;
};